package com.google.code.jtasks.client.ui.commands;

import com.google.code.jtasks.client.exceptions.TerminateException;
import com.google.code.jtasks.client.services.TaskService;
import com.google.code.jtasks.client.ui.Command;
import com.google.code.jtasks.client.ui.ScreenHelper;
import com.google.code.jtasks.client.ui.TaskOutputter;
import com.google.code.jtasks.common.data.Task;
import com.google.common.base.Optional;
import com.google.inject.Inject;

import java.util.List;

/**
 * Команда удаления задач. При этом сначала задачи ищутся по подстроке в их именах.
 */
public class DeleteTaskCommand implements Command {
    private final ScreenHelper helper;
    private final TaskOutputter outputter;
    private final TaskService taskService;

    @Inject
    DeleteTaskCommand(ScreenHelper helper, TaskOutputter outputter, TaskService taskService) {
        this.helper = helper;
        this.outputter = outputter;
        this.taskService = taskService;
    }

    @Override
    public String name() {
        return "Delete task";
    }

    @Override
    public String description() {
        return "Delete a task, searching by its name";
    }

    @Override
    public void execute() throws TerminateException {
        int y = 2;

        Optional<String> name = readName(y);
        if (!name.isPresent()) {
            return;
        }
        y += 3;

        List<Task> tasks = findTasks(y, name.get());
        y += 2;
        if (tasks.isEmpty()) {
            helper.drawString(5, y, "No tasks have been found.");
            y += 2;
        } else {
            helper.drawString(5, y, "Found these tasks:");
            y += 2;
            y = outputter.printTasksWithNumbers(y, tasks);

            Optional<Integer> taskNumber = askTaskNumber(y, tasks.size());
            y += 2;
            if (taskNumber.isPresent()) {
                deleteTask(y, tasks.get(taskNumber.get()));
                y += 2;
            } else {
                return;
            }
        }

        helper.waitForKey(y);
    }

    /**
     * Вызывает {@link TaskService} и удаляет указанную задачу, выводя при этом информационные сообщения.
     *
     * @param y вертикальное смещение, на котором выводить сообщение
     * @param task задача, которую следует удалить
     */
    private void deleteTask(int y, Task task) {
        String message = "Deleting task... ";
        helper.drawString(5, y, message);
        helper.refresh();

        taskService.deleteTask(task);

        helper.drawString(5 + message.length() + 1, y, "Done.");
        helper.refresh();
    }

    /**
     * Запрашивает у пользователя число, проверяя правильность его ввода и ограниченность его заданным числом.
     *
     * @param y вертикальное смещение, относительно которого выводить сообщения
     * @param size максимальная величина, которую может ввести пользователь (не включительно)
     * @return введённое пользователем число (корректное), либо отсутствующее значение, если пользователь
     *         отказался от ввода
     */
    private Optional<Integer> askTaskNumber(int y, int size) {
        helper.drawString(5, y, "Enter number of the task you want to delete:");

        Optional<Integer> taskNumber = Optional.absent();
        while (!taskNumber.isPresent()) {
            helper.clearLine(y+1);
            helper.refresh();

            Optional<String> input = helper.readStringWithOutputAt(5, y+1);
            if (!input.isPresent()) {
                break;
            }

            boolean ok = true;

            try {
                taskNumber = Optional.of(Integer.parseInt(input.get()));
                if (taskNumber.get() >= size) {
                    ok = false;
                }
            } catch (NumberFormatException e) {
                ok = false;
            }

            if (ok) {
                break;
            } else {
                helper.drawString(5, y+3, "Invalid input, try again");
                helper.refresh();
            }
        }

        helper.clearLine(y+3);
        return taskNumber;
    }

    /**
     * Ищет с помощью {@link TaskService} задачу по подстроке в имени, при этом выводя информационное сообщение.
     *
     * @param y смещение по вертикали, на котором выводить сообщение
     * @param name подстрока в имени задачи
     * @return список подходящих задач
     */
    private List<Task> findTasks(int y, String name) {
        String message = "Loading tasks...";
        helper.refresh();
        helper.drawString(5, y, message);

        List<Task> tasks = taskService.findTaskByName(name);

        helper.drawString(5 + message.length() + 1, y, "Done.");
        helper.refresh();
        return tasks;
    }

    /**
     * Считывает подстроку, которую следует искать в именах задач, вместе с сопутствующим выводом сообщения.
     *
     * @param y смещение по вертикали, на котором выводить сообщение
     * @return {@code Optional.of(значение)}, если было введение значение и был нажат Enter, {@code Optional.absent()},
     *         если был нажат Escape
     */
    private Optional<String> readName(int y) {
        helper.drawString(5, y, "Enter part of task name:");
        return helper.readStringWithOutputAt(5, y+1);
    }
}
